/////////////////////////////////////////////////////////////////////////////////
//
//  QAFDebug critical error log
//
//  Copyright (c) 2000-2004
//  Andrew Schetinin
//
//  This software is provided "as is" without express or implied warranty,
//  and with no claim as to its suitability for any purpose.
//
//  Permission to use or copy this software for any purpose is hereby granted
//  without fee, provided the above notices are retained on all copies.
//  Permission to modify the code and to distribute modified code is granted,
//  provided the above notices are retained, and a notice that the code was
//  modified is included with the above copyright notice.
//
//  This software accompanies the article "Code that debugs itself"
//  located at http://www.codeproject.com/debug/qafdebug.asp
//
//  You are welcomed to report bugs, send comments and post code modifications
//  to aschetinin@hotmail.com
//
/////////////////////////////////////////////////////////////////////////////////

///
/// @file	qafdebug.cpp "../src/qafdebug.cpp"
/// @brief	Implementation of functions for reporting critical errors.
///
///			This is the implementation of the "qafdebug.h" macros.
///

// Windows-specific code.
// It cannot be wrapped in the #ifdef WIN32 because VC++ does not work correctly
// with pre-processing directives in this case.

#include "stdafx.h"

// Commmon code

#include "qafdebug.h"

#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>

// Windows-specific code
#ifdef WIN32

	#include <io.h>
	#include <direct.h>
	#include <shlobj.h>

	// In order to link to version.lib automatically
	#pragma message("Automatic link to version.lib")
	#pragma comment(lib, "version.lib")
    #pragma warning(disable:4267) 

	const int R_OK = 0;
	const int W_OK = 0;

	const Q_TCHAR PATH_DELIMITER = _T('\\');
	const Q_TCHAR PATH_DELIMITER_STR[] = _T("\\");

	#define EOL _T("\r\n")

	/// Subfolder in the application data folder
	const Q_LPCTSTR QAFDEBUG_LOG_SUBFOLDER = _T(QAFDEBUG_COMPANY_NAME) _T("\\Log\\");
	
// Linux-specific code
#else

	#include <sys/time.h>
	#include <sys/ipc.h>
	#include <sys/sem.h>
	#include <errno.h>
	#include <unistd.h>

	const Q_TCHAR PATH_DELIMITER = _T('/');

	#define EOL _T("\n")

	#define _tfopen fopen
	#define _stat stat
	#define _tstat stat
	#define _tremove remove
	#define _trename rename
	#define _taccess access

	/// Subfolder in the application data folder
	const Q_LPCTSTR QAFDEBUG_LOG_SUBFOLDER = _T("/var/log/") _T(QAFDEBUG_COMPANY_NAME) _T("/");

#endif

// Only if reporting is not disabled
#ifndef QAF_DISABLED

////////////////////////////////////////////////////////////////////////////////////
// Defines and constants
////////////////////////////////////////////////////////////////////////////////////

/// Size of the shared flags stored in the memory-mapped-file
const unsigned int QDEBUG_SHMEMSIZE = sizeof(unsigned long);

/// Buffer length for HRESULT formatting
const unsigned int QAFDEBUG_FMT_HRESULT_LEN = 20;

/// Format string for HRESULT formatting
const Q_LPCTSTR QAFDEBUG_FMT_HRESULT = _T("FAILED(0x%08X)");

/// Buffer length for ERROR_SUCCESS formatting
const unsigned int QAFDEBUG_FMT_ERROR_SUCCESS_LEN = 30;

/// Format string for ERROR_SUCCESS formatting
const Q_LPCTSTR QAFDEBUG_FMT_ERROR_SUCCESS = _T("(ERROR_SUCCESS != 0x%08X)");

// DebugOutString() for saving to file:
//
// C:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(137) : Assertion raised
//         time:        2003-12-07 15:48:07:507
//         process:     0x00000E24
//         thread:      0x00000E2C
//         application: C:\Program Files\Microsoft Office\Office10\WINWORD.EXE <1.4.6.7>
//         module:      C:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.4.6.7>
//         last error:  6, The handle is invalid.
//         expression:  DuplicateHandle( NULL, NULL, NULL, NULL, 0, FALSE, 0 )

// IMPORTANT:
//            I limit here format string output to be safe in the buffer of 2048 characters.
//            Otherwise the buffer may be overrun.

/// Maximum size of the debug record.
const unsigned int MAX_BUF_SIZE = 2048;

/// 3 parameters
/// "filename(line) : error message"
#define QAFDEBUG_FMT_SPOS _T("%.250s(%d) : %.600s")

/// End of line + tab
#define QAFDEBUG_FMT_PREFIX EOL _T("\t")

/// 7 parameters - formatting date and time
/// "        time:        2003-12-07 15:48:07:507"
#define QAFDEBUG_FMT_DATE QAFDEBUG_FMT_PREFIX _T("time:        ") _T("%04d-%02d-%02d %02d:%02d:%02d:%03d")

/// 1 parameter - formatting process info
/// "        process:     0x00000E24"
#define QAFDEBUG_FMT_PROC QAFDEBUG_FMT_PREFIX _T("process:     ") _T("0x%08X")

/// 1 parameter - formatting thread info
/// "        thread:      0x00000E2C"
#define QAFDEBUG_FMT_THRE QAFDEBUG_FMT_PREFIX _T("thread:      ") _T("0x%08X")

/// 1 parameter - formatting application info
/// "        application: C:\Program Files\Microsoft Office\Office10\WINWORD.EXE <1.4.6.7>"
#define QAFDEBUG_FMT_APPL QAFDEBUG_FMT_PREFIX _T("application: ") _T("%.250s")

/// 1 parameter - formatting module info
/// "        module:      C:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.4.6.7>"
#define QAFDEBUG_FMT_MODU QAFDEBUG_FMT_PREFIX _T("module:      ") _T("%.250s")

/// 1 parameter - formatting last error info
/// "        last error:  6, The handle is invalid."
#define QAFDEBUG_FMT_LERR QAFDEBUG_FMT_PREFIX _T("last error:  ") _T("%.250s")

/// 1 parameter - formatting expression info
/// "        expression:  DuplicateHandle( NULL, NULL, NULL, NULL, 0, FALSE, 0 )"
#define QAFDEBUG_FMT_EXPR QAFDEBUG_FMT_PREFIX _T("expression:  ") _T("%.250s")

/// The resulting format strings
/// 16 parameters
#define QAFDEBUG_STD_FORMAT \
			QAFDEBUG_FMT_SPOS \
			QAFDEBUG_FMT_DATE \
			QAFDEBUG_FMT_PROC \
			QAFDEBUG_FMT_THRE \
			QAFDEBUG_FMT_APPL \
			QAFDEBUG_FMT_MODU \
			QAFDEBUG_FMT_LERR \
			QAFDEBUG_FMT_EXPR

// Fixed error messages for faults in the debug engine
#define QAFDEBUG_ERROR_MUTEX_CREATE       QAFDEBUG_ERROR_PREFIX _T("Mutex is not created") EOL
#define QAFDEBUG_ERROR_MAP_FILE_CREATE    QAFDEBUG_ERROR_PREFIX _T("Mapped-memory file is not created") EOL
#define QAFDEBUG_ERROR_LOG_FILE_PATH      QAFDEBUG_ERROR_PREFIX _T("Path to the error log file cannot be retrieved") EOL
#define QAFDEBUG_ERROR_POINTER_NOT_MAPPED QAFDEBUG_ERROR_PREFIX _T("Memory pointer is not mapped to the Mapped-memory file") EOL
#define QAFDEBUG_ERROR_NULL               QAFDEBUG_ERROR_PREFIX _T("qafOutputDebugString() got an empty error message.") EOL
#define QAFDEBUG_ERROR_STD_NULL           QAFDEBUG_ERROR_PREFIX _T("QAFDebug::std pointer is not initialized.") EOL
#define QAFDEBUG_ERROR_OPEN_LOG_FILE      QAFDEBUG_ERROR_PREFIX _T("Debug log file cannot be opened.") EOL
#define QAFDEBUG_ERROR_ALLOCATE_BUFFER    QAFDEBUG_ERROR_PREFIX _T("Cannot allocate memory for formatting the error message.") EOL
#define QAFDEBUG_ERROR_NULL_ARGS          QAFDEBUG_ERROR_PREFIX _T("NULL input parameters for formatting the error message.") EOL
#define QAFDEBUG_ERROR_FORMAT             QAFDEBUG_ERROR_PREFIX _T("Cannot format the error message.") EOL
#define QAFDEBUG_ERROR_CLOSE_FILE         QAFDEBUG_ERROR_PREFIX _T("Cannot close the debug log file") EOL
#define QAFDEBUG_ERROR_BACKUP             QAFDEBUG_ERROR_PREFIX _T("Cannot create a backup copy of the debug log file") EOL

////////////////////////////////////////////////////////////////////////////////////
// Service Functions
////////////////////////////////////////////////////////////////////////////////////

///
/// @fn ODS
/// @brief Replacement for OutputDebugString() with checking that it is enabled.
///
inline void ODS( Q_LPCTSTR szMessage )
{
	#ifndef QAF_OUTPUTDEBUGSTRING_DISABLED
		#ifdef WIN32
			OutputDebugString( szMessage );
		#else
			fprintf( stderr, szMessage );
		#endif
	#endif
}

////////////////////////////////////////////////////////////////////////////////////
// Classes
////////////////////////////////////////////////////////////////////////////////////

/*
 * This code implements the AUTODIN II polynomial
 * The variable corresponding to the macro argument "crc" should
 * be an unsigned long.
 * Original code  by Spencer Garrett <srg@quick.com>
 */

/* generated using the AUTODIN II polynomial
 *	x^32 + x^26 + x^23 + x^22 + x^16 +
 *	x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
 */

typedef unsigned long d3crc32_t;

#define _CRC32_(crc, ch)	 (crc = (crc >> 8) ^ crc32tab[(crc ^ (ch)) & 0xff])

static const d3crc32_t crc32tab[256] =
{
	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
	0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
	0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
	0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
	0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
	0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
	0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
	0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
	0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
	0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
	0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
	0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
	0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
	0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
	0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
	0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
	0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};

d3crc32_t D3CRC32( char const * const buf, const size_t size )
{
	d3crc32_t crc = (d3crc32_t) ~ 0;

	if( NULL != buf )
	{
		char const *p = buf;
		for( size_t nr = size; nr--; ++p )
			_CRC32_( crc, *p );
	}

	return ~crc;
}

#ifndef WIN32
	/// Critical section type simulation for Linux systems - it uses
	typedef long CRITICAL_SECTION;
#endif

/// A simple synchronization class, designed to be used as an automatic variable.
/// Once its instance is instanciated, it locks its visibility scope.
class CSync
{
public:

	/// Flag that the critical section object must be constructed or destroyed
	enum CSOP { CS_NOP = 0, CS_CREATE = 1, CS_DESTROY = 2 };

	/// Constructor creates and enters the critical section
	CSync( CRITICAL_SECTION * pcs, const CSOP csop = CS_NOP )
	{
		m_pcs = pcs;
		m_csop = csop;
		#ifdef WIN32
		if( CS_CREATE == m_csop )
			InitializeCriticalSection( m_pcs );
		EnterCriticalSection( m_pcs );
		#endif
	}

	/// Destructor leaves and destroys the critical section
	~CSync()
	{
		#ifdef WIN32
		LeaveCriticalSection( m_pcs );
		if( CS_DESTROY == m_csop )
			DeleteCriticalSection( m_pcs );
		#endif
	}

private:

	/// Critical section is used for synchronization
	CRITICAL_SECTION * m_pcs;

	/// Flag that the critical section object must be constructed or destroyed
	CSOP m_csop;

};

/// Portable class for named mutex - implementation is not finished - problems with Linux
/*
class CMutex
{
public:

	/// Initialize the mutex with the given name
	CMutex( char const * const szMutexName ) : m_mutex( 0 )
	{
		unsigned long unique_key = 0x6d1f207b; // a default constant value
		if( (NULL != szMutexName) && (0 != szMutexName[0]) )
			unique_key = D3CRC32( szMutexName, strlen( szMutexName ) );
		m_mutex = semget( unique_key, 1, IPC_CREAT | 0666 );
		if( -1 == m_mutex )
			m_mutex = 0;
	}

	/// Destructor
	~CMutex()
	{
		if( 0 != m_mutex )
			m_mutex = 0;
	}

	/// Lock the mutex - wait indefenitly
	bool lock()
	{
		return lock( 0xFFffFFff );
	}

	/// Lock the mutex
	bool lock( unsigned long wait_msec )
	{
		bool bNoWait = (0 == wait_msec);
		struct sembuf buf;
		buf.sem_num = 0;
		buf.sem_op = -1;
		buf.sem_flg = SEM_UNDO | ( bNoWait ? IPC_NOWAIT : 0 );
		long nRet = -1;
		if( bNoWait )
			nRet = semop( m_mutex, &buf, 1 );
		else
		{
			timespec t;
			nRet = semtimedop( m_mutex, &buf, 1, &t );
		}
		return -1 != nRet;
	}

	/// Unlock the mutex
	bool unlock()
	{
		struct sembuf buf;
		buf.sem_num = 0;
		buf.sem_op = 1;
		buf.sem_flg = IPC_NOWAIT;
		long nRet = semop( m_mutex, &buf, 1 );
		return -1 != nRet;
	}

protected:

	/// Id of the mutex object
	long m_mutex;

	/// Default constructor - disabled
	CMutex()
	{
	}

	/// Copy constructor - disabled
	CMutex( const CMutex & obj )
	{
	}

	/// Assignment operator - disabled
	CMutex & operator=( const CMutex & obj )
	{
		if( this != &obj )
		{
		}
		return *this;
	}

private:
};
*/

/*
bool test_mutex()
{
	CMutex mutex( "tm1" );
	if( !mutex.lock() )
		return false;
	mutex.unlock();
	return true;
}
*/

/// Portable function that returns process id
inline unsigned long get_process_id()
{
	#ifdef WIN32
		return GetCurrentProcessId();
	#else
		return getpid();
	#endif
}

/// Portable function that returns process id
inline unsigned long get_thread_id()
{
	#ifdef WIN32
		return GetCurrentThreadId();
	#else
		return 0;
	#endif
}

///
/// @class CQAFDebug
/// @brief Class to generate debug log
///
class CQAFDebug
{
private:

	#ifndef QAF_UNITTEST_DISABLED
	/// This is a named mutex that regulates access to the memory-mapped-file.
	HANDLE m_hMutex;

	/// Pointer to the shared memory that contains the shared flags.
	void * m_pMem;

	/// Handle of the shared memory-mapped-file.
	HANDLE m_hMapObject;
	#endif

	/// Name of the log file, it is initialized by the constructor
	#ifndef QAF_LOGFILE_DISABLED
	Q_TCHAR m_szLogFileName[Q_MAX_PATH];
	#endif

	/// Critical section is used for synchronization the class methods
	CRITICAL_SECTION m_cs;

	/// Application name
	Q_TCHAR m_szApplicationName[Q_MAX_PATH];

protected:

	#ifndef QAF_UNITTEST_DISABLED
	/// Set the shared flag in the memory mapped file
	bool SetSharedFlag( const unsigned long dwFlag )
	{
		if( ! tryLock() )
			return false;

		memcpy( m_pMem, &dwFlag, sizeof(dwFlag) );

		unLock();

		return true;
	}

	/// Get the shared flag from the memory mapped file
	void CQAFDebug::GetSharedFlag( unsigned long * pdwFlag )
	{
		if( (NULL != pdwFlag) && tryLock() )
		{
			memcpy( reinterpret_cast<void *>(pdwFlag), m_pMem, sizeof(*pdwFlag) );
			unLock();
		}
	}

	/// Try to lock the memory mapped file.
	bool tryLock()
	{
		#ifdef WIN32
		return (WaitForSingleObject( m_hMutex, 50 ) == WAIT_OBJECT_0);
		#else
		return true;
		#endif
	}

	/// Unlock the locked memory mapped file.
	void unLock()
	{
		#ifdef WIN32
		ReleaseMutex( m_hMutex );
		#endif
	}
	#endif

	/// Generate the file name and directory (check that the file is here).
	/// Get the buffer and the buffer length in Q_TCHAR characters including tailing 0x00(00).
	/// Returns the length of the written string or 0 if the file name cannot be generated.
	/// The file name is constructed from the:
	/// 1. Try get the folder path from the environment variable QAFDEBUG_LOG_ENV_VAR
	/// 2. Try CSIDL_APPDATA (C:\Documents and Settings\username\Application Data) + QAFDEBUG_LOG_SUBFOLDER
	/// 3. Try CSIDL_COMMON_APPDATA (C:\Documents and Settings\All Users\Application Data) + QAFDEBUG_LOG_SUBFOLDER
	/// 4. Return 0
	/// If the folders are missing on the disk, they are created.
	static unsigned long qafGetLogFileName( Q_LPTSTR szFilenameBuf, const unsigned long dwMaxLen );

	/// Get the shared instance of the log class
	static CQAFDebug & instance( void )
	{
		// Initialize the static instance (it will be a global variable)
 		static CQAFDebug std_err;
		// Return the instance
		return std_err;
	}

public:

	/// Constructor. It does some complex initialization of memory shared files and instance pointers.
	CQAFDebug()
		#ifndef QAF_UNITTEST_DISABLED
		: m_hMutex(NULL), m_hMapObject(NULL), m_pMem(NULL)
		#endif
	{
		CSync sync( &m_cs, CSync::CS_CREATE );

		if( 0 == SetLogModule( NULL, m_szApplicationName, Q_MAX_PATH ) )
			m_szApplicationName[0] = 0;

		#ifndef QAF_LOGFILE_DISABLED
		// Initialize the file name
		if( 0 == qafGetLogFileName( m_szLogFileName, Q_MAX_PATH ) )
			m_szLogFileName[0] = 0;
		#endif

		#ifndef QAF_UNITTEST_DISABLED
		m_hMutex = CreateMutex( NULL, false, QAFDEBUG_SILENCE_MUTEX );
		if( NULL == m_hMutex )
			ODS( QAFDEBUG_ERROR_MUTEX_CREATE );

		m_hMapObject = CreateFileMapping(
			INVALID_HANDLE_VALUE, // use paging file
			NULL,                 // default security attributes
			PAGE_READWRITE,       // read/write access
			0,                    // size: high 32-bits
			QDEBUG_SHMEMSIZE,     // size: low 32-bits
			QDEBUG_SHMEMFILE );   // name of map object
		if( NULL == m_hMapObject )
			ODS( QAFDEBUG_ERROR_MAP_FILE_CREATE );

		// The first process to attach initializes memory.
		bool fInit = (GetLastError() != ERROR_ALREADY_EXISTS);

		// Get a pointer to the file-mapped shared memory.
		m_pMem = MapViewOfFile(
			m_hMapObject,     // object to map view of
			FILE_MAP_WRITE, // read/write access
			0,              // high offset:  map from
			0,              // low offset:   beginning
			0 );            // default: map entire file
		if( NULL == m_pMem )
			ODS( QAFDEBUG_ERROR_POINTER_NOT_MAPPED );

		if( fInit )
			SetSharedFlag( TRUE );
		#endif
	}

	/// Destructor. It deinitializes the shared flags.
	~CQAFDebug()
	{
		CSync sync( &m_cs, CSync::CS_DESTROY );

		#ifndef QAF_UNITTEST_DISABLED
		unLock();
		#endif

		#ifndef QAF_LOGFILE_DISABLED
		m_szLogFileName[0] = 0;
		#endif

		#ifndef QAF_UNITTEST_DISABLED
		if( NULL != m_pMem )
		{
			UnmapViewOfFile( m_pMem );
			m_pMem = NULL;
		}

		if( NULL != m_hMapObject )
		{
			CloseHandle( m_hMapObject );
			m_hMapObject = NULL;
		}

		if( NULL != m_hMutex )
		{
			CloseHandle( m_hMutex );
			m_hMutex = NULL;
		}
		#endif
	}

	#ifndef QAF_LOGFILE_DISABLED
	/// try open the log file, testing its size and moving it to the same log file if needed.
	static FILE * tryOpenLogFile( void )
	{
		CSync sync( &instance().m_cs );
		Q_LPTSTR szFilename = instance().m_szLogFileName;
		bool bAlreadyTried = false;
		FILE * f = NULL;
		while( true )
		{
			long errcode = _tfopen_s( &f, szFilename, _T("a+b") );
			if( (0 != errcode) || (NULL == f) )
			{
				ODS( QAFDEBUG_ERROR_OPEN_LOG_FILE );
				return NULL;
			}
			struct _stat buf;
			long nRet = _tstat( szFilename, &buf );
			if( bAlreadyTried || ((0 == nRet) && ((QAFDEBUG_LOG_FILE_MAX_SIZE / 2) > buf.st_size)) )
				break;
			if( 0 != fclose( f ) )
			{
				ODS( QAFDEBUG_ERROR_CLOSE_FILE );
				return NULL;
			}
			Q_TCHAR szCopyFilename[Q_MAX_PATH] = { 0 };
			_tcscpy_s( szCopyFilename, Q_MAX_PATH, szFilename );
			Q_LPTSTR szPos = _tcsrchr( szCopyFilename, PATH_DELIMITER );
			szPos[1] = 0;
			_tcscat_s( szPos, Q_MAX_PATH - (szPos - szCopyFilename), QAFDEBUG_LOG_OLD_FILE_NAME );
			_tremove( szCopyFilename ); // ignore the error if the file does not exist (should return 0 on success)
			bool bRet = (0 != _trename( szFilename, szCopyFilename ));
			if( bRet )
			{ // report about the error but not exit - continue using the current file
				ODS( QAFDEBUG_ERROR_BACKUP );
			}
			bAlreadyTried = true;
		}
		return f;
	}
	#endif

	/// try to enable the log
	static bool tryEnable( void )
	{
		#ifdef QAF_UNITTEST_DISABLED
		return true;
		#else
		CSync sync( &instance().m_cs );
		return instance().SetSharedFlag( TRUE );
		#endif
	}

	/// try to disable the log
	static bool tryDisable( void )
	{
		#ifdef QAF_UNITTEST_DISABLED
		return true;
		#else
		CSync sync( &instance().m_cs );
		return instance().SetSharedFlag( FALSE );
		#endif
	}

	/// Check if the debug log is enabled in the memory mapped file
	static bool isEnabled( void )
	{
		#ifdef QAF_UNITTEST_DISABLED
			return true;
		#else
			CSync sync( &instance().m_cs );
			unsigned long dwTemp = FALSE;
			instance().GetSharedFlag( &dwTemp );
			return (dwTemp == TRUE);
		#endif
	}

	/// Return a pointer to a null-terminated string with the application name and version.
	static Q_LPCTSTR GetApplication()
	{
		return instance().m_szApplicationName;
	}

	/// Set the application name to the given string.
	static void SetApplication( Q_LPCTSTR szApplicationName )
	{
		_tcscpy_s( instance().m_szApplicationName, Q_MAX_PATH, szApplicationName );
	}

	/// Get the full path and version number of the given module and record them in
	/// the output buffer.
	/// The function returns the number of characters in the output buffer
	/// or 0 in a case of any error.
	static unsigned long SetLogModule( HMODULE hModule, Q_LPTSTR szOutputBuffer, unsigned long dwBufSize )
	{
		if( (NULL == szOutputBuffer) || (dwBufSize < 2) )
			return 0;
		szOutputBuffer[0] = 0;
		#ifdef WIN32
		unsigned long dwRet = GetModuleFileName( hModule, szOutputBuffer, dwBufSize - 1 );
		if( (0 == dwRet) || (dwRet > (dwBufSize - 2)) )
			return 0;
		unsigned long dwModuleFileNameLen = dwRet;
		unsigned long dwReserved = 0;
		dwRet = GetFileVersionInfoSize( szOutputBuffer, &dwReserved );
		if( 0 == dwRet )
			return dwModuleFileNameLen;
		void * pVerInfo = malloc( dwRet );
		if( NULL == pVerInfo )
			return dwModuleFileNameLen;
		memset( pVerInfo, 0, dwRet );
		BOOL bRet = GetFileVersionInfo( szOutputBuffer, dwReserved, dwRet, pVerInfo );
		if( !bRet )
		{
			free( pVerInfo );
			return dwModuleFileNameLen;
		}
		unsigned int uiSize = 0;
		VS_FIXEDFILEINFO * pFixedVerInfo = NULL;
		bRet = VerQueryValue( pVerInfo, _T("\\"), (LPVOID *)(&pFixedVerInfo), &uiSize );
		if( !bRet || (NULL == pFixedVerInfo) || (sizeof(VS_FIXEDFILEINFO) != uiSize) )
		{
			free( pVerInfo );
			return dwModuleFileNameLen;
		}
		const long MAX_TEMP_BUF = 26;
		Q_TCHAR szBuf[MAX_TEMP_BUF] = { 0 };
		#if defined(WIN32) && (_MSC_VER >= 1400)
			_sntprintf_s( szBuf, MAX_TEMP_BUF - 1, MAX_TEMP_BUF - 1, _T(" <%d.%d.%d.%d>"),
		#else
			_sntprintf( szBuf, MAX_TEMP_BUF - 1, _T(" <%d.%d.%d.%d>"),
		#endif
			HIWORD(pFixedVerInfo->dwFileVersionMS), LOWORD(pFixedVerInfo->dwFileVersionMS),
			HIWORD(pFixedVerInfo->dwFileVersionLS), LOWORD(pFixedVerInfo->dwFileVersionLS) );
		szBuf[MAX_TEMP_BUF - 1] = 0;
		free( pVerInfo );
		if( (dwModuleFileNameLen + MAX_TEMP_BUF + 1) < dwBufSize )
		{
			_tcscpy_s( szOutputBuffer + dwModuleFileNameLen, 
				dwBufSize - (dwModuleFileNameLen + MAX_TEMP_BUF + 1), szBuf );
			dwModuleFileNameLen += _tcslen( szBuf );
		}
		return dwModuleFileNameLen;
		#else
		return 0;
		#endif
	}

};

/// Linux and Windows class for getting the current time up to msec
class CSysTime
{
public:

	/// Stores msec value of the last call to Now() or the class instance creation.
	unsigned int msec;

	/// Stores sec value of the last call to Now() or the class instance creation.
	unsigned int sec;

	/// Stores min value of the last call to Now() or the class instance creation.
	unsigned int min;

	/// Stores hour value of the last call to Now() or the class instance creation.
	unsigned int hour;

	/// Stores day value of the last call to Now() or the class instance creation.
	unsigned int day;

	/// Stores month value of the last call to Now() or the class instance creation.
	unsigned int month;

	/// Stores year value of the last call to Now() or the class instance creation.
	unsigned int year;

	/// Default constructor. It calculates and stores the time of the class instance creation.
	CSysTime()
	{
		Now();
	}

	/// This function calculates and stores the current time.
	void Now()
	{
		#ifdef WIN32 /// Windows-specific implementation
			SYSTEMTIME st;
			GetLocalTime( &st );
			msec = st.wMilliseconds;
			sec = st.wSecond;
			min = st.wMinute;
			hour = st.wHour;
			day = st.wDay;
			month = st.wMonth;
			year = st.wYear;
		#else /// Linux-specific implementation
			struct timeval tp; // time with msec
			time_t tmt;        // time for broking
			struct tm tb;      // broken time
			gettimeofday( &tp, NULL );
			tmt = time( NULL );
			localtime_r( &tmt, &tb );
			msec = tp.tv_usec / 1000; // from microseconds to msec
			sec = tb.tm_sec;
			min = tb.tm_min;
			hour = tb.tm_hour;
			day = tb.tm_mday;
			month = tb.tm_mon;
			year = tb.tm_year + 1900; // They should count from 1970 but use 1900 instead
		#endif
	}

protected:
private:

	/// Copy constructor - disabled
	CSysTime( const CSysTime & obj )
	{
		assert( false );
	}

	/// Assignment operator - disabled
	CSysTime & operator=( const CSysTime & obj )
	{
		assert( false );
		return *this;
	}
};

////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////

/// Return true if the directory exists.
bool DirExists0( Q_LPCTSTR dirName )
{
	return 0 == _taccess_s( dirName, R_OK | W_OK );
}

/// Create the full folder path recursively.
/// Must get the folder full path without tailing '\'.
bool CreateSubFolders0( Q_LPCTSTR szFolderName )
{
	// Prepare variables
	unsigned long dwLen = _tcslen(szFolderName);
	if( dwLen <= 0 )
		return false;
	// Copy the input string with removing the trailing '\'
	Q_LPTSTR szTemp = (Q_LPTSTR) malloc( (dwLen + 1) * sizeof(Q_TCHAR) );
	if( NULL == szTemp )
		return false;
	_tcscpy_s( szTemp, dwLen + 1, szFolderName );
	if( szTemp[dwLen - 1] == PATH_DELIMITER )
		dwLen--;
	szTemp[dwLen] = _T('\0');
	// If the directory exists, that's it!
	bool bRet = DirExists0( szTemp );
	// If the directory does not exists, check its parent
	if( ! bRet )
	{
		Q_LPTSTR szPos = _tcsrchr( szTemp, PATH_DELIMITER );
		if( NULL != szPos )
		{
			(*szPos) = _T('\0');
			// Call the function recursively
			if( CreateSubFolders0( szTemp ) )
			{
				// Now try to create the subfolder and exit
				(*szPos) = PATH_DELIMITER;
				#ifdef WIN32
					bRet = (0 == _tmkdir( szTemp ));
				#else
					bRet = (0 == mkdir( szTemp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH )) || (EEXIST == errno);
				#endif
			}
		}
	}
	free( szTemp );
	return bRet;
}

// Taken from the platform SDK
#if !defined(CSIDL_COMMON_APPDATA)
	// All Users\Application Data
	#define CSIDL_COMMON_APPDATA 0x0023
#endif
#if !defined(CSIDL_FLAG_CREATE)
	// new for Win2K, to force creation of the folder
	#define CSIDL_FLAG_CREATE 0x8000
#endif

// This part is excluded for Windows CE and Linux
#if !defined(_WIN32_WCE) && defined(WIN32)

/// Count of the libraries with the SHGetFolderPath function
const long LIBRARY_COUNT = 2;

/// The first DLL that is checked for the SHGetFolderPathA(W) function
const Q_LPCTSTR LIBRARY_DLL[LIBRARY_COUNT] = { _T("shell32.dll"), _T("shfolder.dll") };

/// The name of the function (it differs for ASCII and UNICODE builds)
#ifdef _UNICODE
	const Q_LPCSTR SHGetFolderPathFuncName = "SHGetFolderPathW";
#else
	const Q_LPCSTR SHGetFolderPathFuncName = "SHGetFolderPathA";
#endif

/// The folder search function definition (taken from MSDN)
typedef HRESULT (__stdcall* PSHGetFolderPath)
	( HWND hwndOwner, long nFolder, HANDLE hToken, unsigned long dwFlags, Q_LPTSTR szPath );

/// My wrapper function that tries to load first shell32.dll and then shfolder.dll
HRESULT SHGetSpecialFolderPathCustom( HWND hwndOwner, long nFolder, HANDLE hToken, unsigned long dwFlags, Q_LPTSTR szPath )
{
	HRESULT hr = E_FAIL;
	for( long i = 0; i < LIBRARY_COUNT; i++ )
	{
		HMODULE hModule = LoadLibrary( LIBRARY_DLL[i] );
		if( NULL != hModule )
		{
			PSHGetFolderPath pFunc = (PSHGetFolderPath) GetProcAddress( hModule, SHGetFolderPathFuncName );
			if( NULL != pFunc )
				hr = pFunc( hwndOwner, nFolder, hToken, dwFlags, szPath );
			FreeLibrary( hModule );
		}
		if( S_OK == hr ) // I cannot test for SUCCEEDED because it may return S_FALSE on error (look in MSDN)
			break;
	}
	return hr;
}

#endif // This part is excluded for Windows CE and Linux

/// This is an enumeration from MSDN for newer versions of SHxxx functions.
/// Renamed to be ported easily between VC 6 and VC 7
typedef enum {
	Q_SHGFP_TYPE_CURRENT  = 0,   ///< current value for user, verify it exists
	Q_SHGFP_TYPE_DEFAULT  = 1,   ///< default value, may not exist
} Q_SHGFP_TYPE;

/// Generate the file name and directory (check that the file is here).
/// Get the buffer and the buffer length in Q_TCHAR characters including tailing 0x00(00).
/// Returns the length of the written string or 0 if the file name cannot be generated.
/// The file name is constructed from the:
/// 1. Try get the folder path from the environment variable QAFDEBUG_LOG_ENV_VAR
/// 2. Try CSIDL_APPDATA (C:\Documents and Settings\username\Application Data) + QAFDEBUG_LOG_SUBFOLDER
/// 3. Try CSIDL_COMMON_APPDATA (C:\Documents and Settings\All Users\Application Data) + QAFDEBUG_LOG_SUBFOLDER
/// 4. Return 0
/// If the folders are missing on the disk, they are created.
unsigned long CQAFDebug::qafGetLogFileName( Q_LPTSTR szFilenameBuf, const unsigned long dwMaxLen )
{
	unsigned long dwRet = QAFDebug::GetLogDir( szFilenameBuf, dwMaxLen );
	if( 0 == dwRet )
		return 0;
	// If the directory is created, it is o'key and we can continue with the file name
	_tcscat_s( szFilenameBuf, dwMaxLen, QAFDEBUG_LOG_FILE_NAME );
	return _tcslen(szFilenameBuf);
}


/////////////////////////////////////////////////////////////////////////////////////////////////
/// Small class that preserves the last error code
/////////////////////////////////////////////////////////////////////////////////////////////////
class CPreserveLastError
{
public:

	/// Constructor saves the last error code or inherits the error code from a chained
	/// instance of this class.
	CPreserveLastError( const unsigned long * pdwLastError )
	{
		m_bPreserveLastError = (NULL == pdwLastError);
		if( m_bPreserveLastError )
			#ifdef WIN32
			m_dwLastError = GetLastError();
			#else
			m_dwLastError = errno;
			#endif
		else
			m_dwLastError = (*pdwLastError);
	}

	/// Destructor restores the saved last error code if it is a self-enough instance.
	~CPreserveLastError()
	{
		#ifdef WIN32
		if( m_bPreserveLastError && (GetLastError() != m_dwLastError) )
			SetLastError( m_dwLastError );
		#else
		if( m_bPreserveLastError && (errno != m_dwLastError) )
			errno = m_dwLastError;
		#endif
	}

	/// Return the last saved error code.
	unsigned long GetSavedLastError( void )
	{
		return m_dwLastError;
	}

	/// Return a pointer to the last saved error code
	operator unsigned long*()
	{
		return &m_dwLastError;
	}

	/// Return the error message
	void FormatErrorMessage( Q_LPTSTR szBuffer, unsigned long dwBufLen )
	{
		if( (NULL == szBuffer) || (dwBufLen <= 1) )
			return;
		if( 0 == GetSavedLastError() )
		{
			szBuffer[0] = _T('0');
			szBuffer[1] = 0;
			return;
		}
		#if defined(WIN32) && (_MSC_VER >= 1400)
			_stprintf_s( szBuffer, dwBufLen, _T("%d, "), GetSavedLastError() );
		#else
			_stprintf( szBuffer, _T("%d, "), GetSavedLastError() );
		#endif
		unsigned long dwLen = _tcslen( szBuffer );
		szBuffer += dwLen;
		dwBufLen -= dwLen;
		#ifdef WIN32
		unsigned long dwRet = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL, GetSavedLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
			szBuffer, dwBufLen, NULL );
		szBuffer[dwRet] = 0;
		while( dwRet-- && _tcschr( EOL, szBuffer[dwRet] ) ) // first compare to 0 and then decrement dwRet!
			szBuffer[dwRet] = 0;
		#else
		unsigned long e = GetSavedLastError();
		#ifdef __USE_GNU
		  //alksdalksjdl = 1;  FUCK!!!
		#endif
		strcpy( szBuffer, strerror( GetSavedLastError() ) );
		//long i = strerror_r( GetSavedLastError(), szBuffer, dwBufLen );
		#endif
	}

private:

	/// Saved last error code
	unsigned long m_dwLastError;

	/// Flag that indicates that the instance is active.
	bool m_bPreserveLastError;

};

/////////////////////////////////////////////////////////////////////////////////////////////////
// QAFDebug namespace
/////////////////////////////////////////////////////////////////////////////////////////////////

/// The current module name - internal variable.
Q_TCHAR szCurrentModuleName[Q_MAX_PATH] = { 0 };

/// Set the current module name (not NULL module name will enable the version logging).
void QAFDebug::SetModule( Q_LPCTSTR szModuleName )
{
	szCurrentModuleName[0] = 0;
	#ifdef WIN32
		if( (NULL != szModuleName) && (0 != szModuleName[0]) )
		{
			HMODULE hModule = GetModuleHandle( szModuleName );
			if( NULL == hModule )
				return;
			CQAFDebug::SetLogModule( hModule, szCurrentModuleName, Q_MAX_PATH );
		}
	#else
		#ifdef PACKAGE
			strcat( szCurrentModuleName, PACKAGE );
		#endif
		#ifdef VERSION
			strcat( szCurrentModuleName, " <" );
			strcat( szCurrentModuleName, VERSION );
			strcat( szCurrentModuleName, ">" );
		#endif
		if( (NULL != szModuleName) && (0 != szModuleName[0]) )
		{
			CQAFDebug::SetApplication( szModuleName );
		}
	#endif
}

bool QAFDebug::tryEnable(void)
{
	return CQAFDebug::tryEnable();
}

bool QAFDebug::tryDisable(void)
{
	return CQAFDebug::tryDisable();
}

/// Return true if the character is \n or \r
inline bool isRN( const Q_TCHAR ch )
{
	return (_T('\r') == ch) || (_T('\n') == ch);
}

void QAFDebug::OutputDebugStringEx( Q_LPCTSTR szFilename, const long iLine, Q_LPCTSTR szExpression,
		Q_LPCTSTR szErrorMessage, const unsigned long * pdwLastError )
{
	// Restore the last error upon exit
	CPreserveLastError LastError( pdwLastError );

	// Check if the log is disabled by unit tests, in this case skip reporting
	if( !CQAFDebug::isEnabled() )
		return;

	// test input parameters
	if( NULL == szFilename )
		szFilename = _T("");
	if( NULL == szExpression )
		szExpression = _T("");
	if( NULL == szErrorMessage )
		szErrorMessage = _T("");

	// C:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(137) : Assertion raised
	//         time:        2003-12-07 15:48:07:507
	//         process:     0x00000E24
	//         thread:      0x00000E2C
	//         application: C:\Program Files\Microsoft Office\Office10\WINWORD.EXE <1.4.6.7>
	//         module:      C:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.4.6.7>
	//         last error:  6, The handle is invalid.
	//         expression:  DuplicateHandle( NULL, NULL, NULL, NULL, 0, FALSE, 0 )

	// Prepare parameters for the complete error message
	CSysTime time; // it calculates the current time
	unsigned long dwProcess = get_process_id(), dwThread = get_thread_id();

	// Decode the last error
	Q_TCHAR szLastError[Q_MAX_PATH] = { 0 };
	LastError.FormatErrorMessage( szLastError, Q_MAX_PATH );

	Q_LPTSTR szFmtStr = QAFDEBUG_STD_FORMAT; // Format string, its length should be enough for decimal values too
	size_t nBufSize = _tcslen( szFmtStr ) + _tcslen( szFilename ) + _tcslen( szExpression )
		+ _tcslen( szErrorMessage ) + _tcslen( szLastError )
		+ _tcslen( CQAFDebug::GetApplication() ) + _tcslen( szCurrentModuleName ); // In Q_TCHAR characters!
	if( nBufSize > MAX_BUF_SIZE )
		nBufSize = MAX_BUF_SIZE;
	Q_LPTSTR szBuffer = (Q_LPTSTR) malloc( nBufSize * sizeof(Q_TCHAR) );
	if( NULL == szBuffer ) // if the memory cannot be allocated, the function fails
	{
		ODS( QAFDEBUG_ERROR_ALLOCATE_BUFFER );
		return;
	}

	// Format the complete error message
	#if defined(WIN32) && (_MSC_VER >= 1400)
		long iRes = _sntprintf_s( szBuffer, nBufSize, nBufSize, szFmtStr,
	#else
		long iRes = _sntprintf( szBuffer, nBufSize, szFmtStr,
	#endif
		szFilename, iLine, szErrorMessage,
		time.year, time.month, time.day, time.hour, time.min, time.sec, time.msec,
		dwProcess,
		dwThread,
		CQAFDebug::GetApplication(),
		szCurrentModuleName,
		szLastError,
		szExpression );
	//unsigned long dw = GetLastError();
	if( 0 >= iRes )
	{
		ODS( QAFDEBUG_ERROR_FORMAT );
		free( szBuffer );
		return;
	}

	// Fix redundant \r
	if( (0 != szBuffer[0]) && (0 != szBuffer[1]) )
	{
		for( Q_TCHAR * szPos = szBuffer; 0 != szPos[2]; ++szPos  )
		{
			if( isRN( szPos[0] ) && isRN( szPos[2] ) )
				szPos[0] = _T(' ');
		}
	}

	// Add the last \r
	unsigned long dwBufferSize = _tcslen( szBuffer );
	if( (szBuffer[dwBufferSize - 1] != _T('\r')) && (szBuffer[dwBufferSize - 1] != _T('\n')) )
	{
		_tcscat_s( szBuffer, nBufSize, EOL );
		dwBufferSize += _tcslen( EOL );
	}

	// Output the complete error message to the Visual Studio IDE
	ODS( szBuffer );

	// If the log file is enabled
	#ifndef QAF_LOGFILE_DISABLED

	// Add another \r for an additional empty line (that simplifies reading)
	_tcscat_s( szBuffer, nBufSize, EOL );
	dwBufferSize += _tcslen( EOL );

	// If requested, encode the error message to UTF8. This define must be set only in UNICODE builds!
	#ifdef QAF_SAVE_UNICODE_AS_UTF8
		unsigned long dwUtf8BufSize = (dwBufferSize + 1) * sizeof(Q_TCHAR);
		Q_LPSTR szUTF8 = (Q_LPSTR) malloc( dwUtf8BufSize );
		if( NULL != szUTF8 ) // in case of memory overflow the UNICODE string will be written to file
		{
			long nRet = WideCharToMultiByte( CP_UTF8, 0, szBuffer, dwBufferSize, szUTF8,
				dwUtf8BufSize, NULL, NULL );
			if( nRet > 0 ) // in case of any error the UNICODE string will be written to file
			{
				free( szBuffer );
				szBuffer = (Q_LPTSTR) szUTF8; // I can do this because this string is used as a plain buffer
				dwBufferSize = nRet; // count of returned bytes - does not include the trailing sero
				// despite the fact that in MSDN it is written that this function return value counts
				// the trailing zero, actually it even does not terminate the output string with \0.
			}
		}
	#else
		dwBufferSize *= sizeof(Q_TCHAR); // I must recalculate here the buffer size because it was in characters
	#endif

	// Output the complete error message to file
	// Try to write to the file during 200 msec (5 times with sleeps within)
	FILE * f = NULL;
	for( long i = 0; i < 5; i++ )
	{
		f = CQAFDebug::tryOpenLogFile();
		if( f != NULL )
		{
#ifndef WIN32
			long fid = fileno( f );
			if( fid >= 0 )
			{
				struct flock fl;
				memset( &fl, 0, sizeof(fl) );
				fl.l_type = F_WRLCK;
				fl.l_whence = SEEK_END;
				/*itimerval val[2];
				memset( val, 0, 2 * sizeof(val[0]) );
				val[0].it_value.tv_usec = 40;
				bool bTimerSet = (0 == setitimer( ITIMER_REAL, &val[0], &val[1] ));*/
				long nRet = fcntl( fid, F_SETLKW, &fl );
				/*if( bTimerSet )
					setitimer( ITIMER_REAL, &val[1], NULL ); // restore the timer to previous value*/
				if( -1 != nRet )
				{
#endif
					fseek( f, 0, SEEK_END );
					fwrite( szBuffer, dwBufferSize, 1, f ); // and I need it in bytes
					fflush( f );
#ifndef WIN32
					memset( &fl, 0, sizeof(fl) );
					fl.l_type = F_UNLCK;
					fl.l_whence = SEEK_END;
					fcntl( fid, F_SETLK, &fl );
				}
			}
#endif
			fclose( f );
			break;
		}
#ifdef WIN32
		Sleep( 40 );
#endif
	}

	// If the log file is enabled
	#endif

	free( szBuffer );
}

#ifdef WIN32

HRESULT QAFDebug::qafReportComError( HRESULT hrStatus, Q_LPCTSTR szFile, const long iLine )
{
	if( SUCCEEDED(hrStatus) )
		return hrStatus;

	CPreserveLastError LastError( NULL );

    if( FACILITY_WINDOWS == HRESULT_FACILITY(hrStatus) )
        hrStatus = HRESULT_CODE(hrStatus);

	Q_LPTSTR szErrMsg = NULL;
	long iFreeErrMsg = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL, hrStatus, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), (Q_LPTSTR)&szErrMsg, 0, NULL );
	if( (0 == iFreeErrMsg) || (NULL == szErrMsg) )
		szErrMsg = QAFDEBUG_ERROR_NO_MESSAGE;

	Q_TCHAR szBuf[QAFDEBUG_FMT_HRESULT_LEN];
	if( 0 >= wsprintf( szBuf, QAFDEBUG_FMT_HRESULT, hrStatus ) )
		szBuf[0] = 0;

	QAFDebug::OutputDebugStringEx( szFile, iLine, szBuf, szErrMsg, static_cast<unsigned long *>(LastError) );

	if( iFreeErrMsg != 0 )
        LocalFree( szErrMsg );

	return hrStatus;
}

unsigned long QAFDebug::ReportWinError( const unsigned long dwError, Q_LPCTSTR szFile, const long iLine )
{
	if( ERROR_SUCCESS == dwError )
		return dwError;

	CPreserveLastError LastError( NULL );

	Q_LPTSTR szErrMsg = NULL;
	long iFreeErrMsg = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL, dwError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), (Q_LPTSTR)&szErrMsg, 0, NULL );
	if( (0 == iFreeErrMsg) || (NULL == szErrMsg) )
		szErrMsg = QAFDEBUG_ERROR_NO_MESSAGE;

	Q_TCHAR szBuf[QAFDEBUG_FMT_ERROR_SUCCESS_LEN];
	if( 0 >= wsprintf( szBuf, QAFDEBUG_FMT_ERROR_SUCCESS, dwError ) )
		szBuf[0] = 0;

	QAFDebug::OutputDebugStringEx( szFile, iLine, szBuf, szErrMsg, static_cast<unsigned long *>(LastError) );

	if( iFreeErrMsg != 0 )
        LocalFree( szErrMsg );

	return dwError;
}

#endif

unsigned long QAFDebug::GetLogDir( Q_LPTSTR szDirBuf, const unsigned long dwMaxLen )
{

// This part is specific for Windows NT and 9x
#if defined(WIN32) && !defined(_WIN32_WCE)

	// Test three different folders, one set from outside, second for regular applications
	// and third for service applications
	for( long i = 1; i <= 3; i++ )
	{
		if( 1 == i )
		{
			// 1. Try get the folder path from the environment variable QAFDEBUG_LOG_ENV_VAR
			unsigned long dwRet = GetEnvironmentVariable( QAFDEBUG_LOG_ENV_VAR, szDirBuf, dwMaxLen );
			if( (dwRet <= 0) || (dwRet >= dwMaxLen) )
				continue; // try the second
		}
		else
		{
			if( 2 == i )
			{
				// 2. Try CSIDL_APPDATA (C:\Documents and Settings\username\Application Data) + QAFDEBUG_LOG_SUBFOLDER
				if( S_OK != SHGetSpecialFolderPathCustom( NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, Q_SHGFP_TYPE_CURRENT, szDirBuf ) )
					continue; // try the third
			}
			else
			{
				// 3. Try CSIDL_COMMON_APPDATA (C:\Documents and Settings\All Users\Application Data) + QAFDEBUG_LOG_SUBFOLDER
				if( S_OK != SHGetSpecialFolderPathCustom( NULL, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, NULL, Q_SHGFP_TYPE_CURRENT, szDirBuf ) )
					return 0; // FAILED!!!
			}
			// Add the relative path
			unsigned long dwSize = _tcslen(szDirBuf);
			if( dwSize > 0 )
			{
				if( szDirBuf[dwSize - 1] != PATH_DELIMITER )
					_tcscat_s( szDirBuf, dwMaxLen, PATH_DELIMITER_STR );
				_tcscat_s( szDirBuf, dwMaxLen, QAFDEBUG_LOG_SUBFOLDER );
			}
		}
		// Test the resulting string
		unsigned long dwSizeBuf = _tcslen(szDirBuf);
		if( 0 == dwSizeBuf )
			continue; // This trial failed

		// Add final '\'
		if( szDirBuf[dwSizeBuf - 1] != PATH_DELIMITER )
			_tcscat_s( szDirBuf, dwMaxLen, PATH_DELIMITER_STR );
		// Check that the directory exists and create missing subdirectories
		if( ! CreateSubFolders0(szDirBuf) )
			continue; // This trial failed
		// Return the result
		return _tcslen(szDirBuf);
	}

#elif defined(WIN32) // This part is specific for Windows CE

	// This function always return FALSE, even if successful.
	SHGetSpecialFolderPath(NULL, szDirBuf, CSIDL_PERSONAL, FALSE);
	// Add the relative path
	unsigned long dwSize = _tcslen(szDirBuf);
	if( dwSize > 0 )
	{
		if( szDirBuf[dwSize - 1] != PATH_DELIMITER )
			_tcscat( szDirBuf, PATH_DELIMITER );
		_tcscat( szDirBuf, QAFDEBUG_LOG_SUBFOLDER );
	}
	// Test the resulting string
	unsigned long dwSizeBuf = _tcslen(szDirBuf);
	if( 0 == dwSizeBuf )
		return 0;

	// Add final '\'
	if( szDirBuf[dwSizeBuf - 1] != PATH_DELIMITER )
		_tcscat( szDirBuf, PATH_DELIMITER );
	// Check that the directory exists and create missing subdirectories
	if( ! CreateSubFolders0(szDirBuf) )
		return 0;

	// Return the result
	return _tcslen(szDirBuf);

#else // This part is specific for Linux

	long nLen = _tcslen( QAFDEBUG_LOG_SUBFOLDER );
	if( (NULL == szDirBuf) || (dwMaxLen < nLen) )
		return 0;
	_tcscpy( szDirBuf, QAFDEBUG_LOG_SUBFOLDER );
	// Check that the directory exists and create missing subdirectories
	if( !CreateSubFolders0( szDirBuf ) )
		return 0;
	return nLen;

#endif

	// FAILED !!!
	return 0;
}

///////////////////////////////////////////////
// END OF FILE
///////////////////////////////////////////////
#endif

